msg_tool\scripts\softpal\img\pgd/
base.rs1use crate::ext::io::*;
2use crate::ext::vec::*;
3use crate::types::*;
4use crate::utils::encoding::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use std::io::{Read, Seek, SeekFrom, Write};
10
11#[derive(Clone, Debug, StructPack, StructUnpack)]
12pub struct PgdGeHeader {
13 pub offset_x: u32,
14 pub offset_y: u32,
15 pub width: u32,
16 pub height: u32,
17 pub canvas_width: u32,
18 pub canvas_height: u32,
19 pub mode: u16,
20 pub _unk: u16,
22}
23
24impl PgdGeHeader {
25 pub fn is_base_file(&self) -> bool {
26 self.offset_x == 0
27 && self.offset_y == 0
28 && self.width == self.canvas_width
29 && self.height == self.canvas_height
30 }
31}
32
33#[derive(Clone, Debug, StructPack, StructUnpack)]
34pub struct PgdDiffHeader {
35 pub offset_x: u16,
36 pub offset_y: u16,
37 pub width: u16,
38 pub height: u16,
39 pub bpp: u16,
40 #[fstring = 0x22]
41 pub base_name: String,
42}
43
44pub struct PgdReader<T: Read + Seek> {
45 pub input: T,
46 output: Vec<u8>,
47 width: u32,
48 height: u32,
49 bpp: u8,
50 method: u16,
51 format: Option<ImageColorType>,
52}
53
54impl<T: Read + Seek> PgdReader<T> {
55 pub fn new(mut input: T, pos: u64) -> Result<Self> {
56 input.seek(SeekFrom::Start(pos))?;
57 let unpacked_size = input.read_u32()?;
58 input.read_u32()?; let output = vec![0u8; unpacked_size as usize];
60 Ok(PgdReader {
61 input,
62 output,
63 width: 0,
64 height: 0,
65 bpp: 0,
66 method: 0,
67 format: None,
68 })
69 }
70
71 pub fn with_ge_header(input: T, header: &PgdGeHeader) -> Result<Self> {
72 let mut s = Self::new(input, 0x20)?;
73 s.width = header.width;
74 s.height = header.height;
75 s.method = header.mode;
76 Ok(s)
77 }
78
79 pub fn with_diff_header(input: T, header: &PgdDiffHeader) -> Result<Self> {
80 let mut s = Self::new(input, 0x30)?;
81 s.width = header.width as u32;
82 s.height = header.height as u32;
83 s.bpp = header.bpp as u8;
84 Ok(s)
85 }
86
87 pub fn unpack_ge(mut self) -> Result<ImageData> {
88 self.unpack_ge_pre()?;
89 let data = match self.method {
90 1 => self.post_process1()?,
91 2 => self.post_process2()?,
92 3 => self.post_process3()?,
93 _ => return Err(anyhow::anyhow!("Unsupported GE mode: {}", self.method)),
94 };
95 let color_type = self
96 .format
97 .ok_or_else(|| anyhow::anyhow!("Unknown image format"))?;
98 Ok(ImageData {
99 width: self.width,
100 height: self.height,
101 color_type,
102 depth: 8,
103 data,
104 })
105 }
106
107 fn unpack_ge_pre(&mut self) -> Result<()> {
108 let mut dst = 0;
109 let mut ctl = 2;
110 let len = self.output.len();
111 while dst < len {
112 ctl >>= 1;
113 if ctl == 1 {
114 ctl = self.input.read_u8()? as i32 | 0x100;
115 }
116 let mut count;
117 if ctl & 1 != 0 {
118 let mut offset = self.input.read_u16()? as usize;
119 count = offset & 7;
120 if offset & 8 == 0 {
121 count = count << 8 | (self.input.read_u8()? as usize);
122 }
123 count += 4;
124 offset >>= 4;
125 self.output.copy_overlapped(dst - offset, dst, count);
126 } else {
127 count = self.input.read_u8()? as usize;
128 self.input.read_exact(&mut self.output[dst..dst + count])?;
129 }
130 dst += count;
131 }
132 Ok(())
133 }
134
135 pub fn unpack_overlay(&mut self) -> Result<ImageData> {
136 self.unpack_ge_pre()?;
137 let data = self.post_process_pal(&self.output, 0, self.bpp as usize / 8)?;
138 let color_type = match self.bpp {
139 24 => ImageColorType::Bgr,
140 32 => ImageColorType::Bgra,
141 _ => {
142 return Err(anyhow::anyhow!(
143 "Unsupported bpp for overlay PGD: {}",
144 self.bpp
145 ));
146 }
147 };
148 Ok(ImageData {
149 width: self.width,
150 height: self.height,
151 color_type,
152 depth: 8,
153 data,
154 })
155 }
156
157 fn post_process1(&mut self) -> Result<Vec<u8>> {
158 self.format = Some(ImageColorType::Bgra);
159 let input = &self.output;
160 let mut output = Vec::with_capacity(input.len());
161 let plane_size = input.len() / 4;
162 let a_src = 0;
163 let r_src = plane_size;
164 let g_src = plane_size * 2;
165 let b_src = plane_size * 3;
166 for i in 0..plane_size {
167 output.push(input[b_src + i]);
168 output.push(input[g_src + i]);
169 output.push(input[r_src + i]);
170 output.push(input[a_src + i]);
171 }
172 Ok(output)
173 }
174
175 #[inline(always)]
176 fn clamp(v: i32) -> u8 {
177 if v > 255 {
178 255
179 } else if v < 0 {
180 0
181 } else {
182 v as u8
183 }
184 }
185
186 fn post_process2(&mut self) -> Result<Vec<u8>> {
187 self.format = Some(ImageColorType::Bgr);
188 let input = &self.output;
189 let stride = self.width as usize * 3;
190 let segment_size = self.width as usize * self.height as usize / 4;
191 let mut src0 = 0;
192 let mut src1 = segment_size;
193 let mut src2 = segment_size * 2;
194 let mut output = vec![0u8; stride * self.height as usize];
195 let mut dst = 0;
196 let points = [0, 1, self.width, self.width + 1];
197 for _y in (1..=(self.height as usize / 2)).rev() {
198 for _x in (1..=(self.width as usize / 2)).rev() {
199 let i0 = input[src0] as i8;
200 let i1 = input[src1] as i8;
201 let b = 226 * i0 as i32;
202 let g = -43 * i0 as i32 - 89 * i1 as i32;
203 let r = 179 * i1 as i32;
204 src0 += 1;
205 src1 += 1;
206 for i in 0..4 {
207 let mut offset = points[i] as usize;
208 let base_value = (input[src2 + offset] as i32) << 7;
209 offset = dst + 3 * offset;
210 output[offset] = Self::clamp((base_value + b) >> 7);
211 output[offset + 1] = Self::clamp((base_value + g) >> 7);
212 output[offset + 2] = Self::clamp((base_value + r) >> 7);
213 }
214 src2 += 2;
215 dst += 6;
216 }
217 src2 += self.width as usize;
218 dst += stride;
219 }
220 Ok(output)
221 }
222
223 fn post_process3(&mut self) -> Result<Vec<u8>> {
224 let input = &self.output;
225 let reader = MemReaderRef::new(input);
226 let bbp = reader.cpeek_u16_at(0x2)?;
227 self.format = Some(if bbp == 24 {
228 ImageColorType::Bgr
229 } else if bbp == 32 {
230 ImageColorType::Bgra
231 } else {
232 return Err(anyhow::anyhow!("Unsupported bpp: {}", bbp));
233 });
234 self.width = reader.cpeek_u16_at(0x4)? as u32;
235 self.height = reader.cpeek_u16_at(0x6)? as u32;
236 self.post_process_pal(input, 8, bbp as usize / 8)
237 }
238
239 fn post_process_pal(&self, input: &[u8], mut src: usize, pixel_size: usize) -> Result<Vec<u8>> {
240 let stride = self.width as usize * pixel_size;
241 let mut output = vec![0u8; stride * self.height as usize];
242 let mut ctl = src;
243 src += self.height as usize;
244 let mut dst = 0;
245 for _row in 0..self.height as usize {
246 let c = input[ctl];
247 ctl += 1;
248 if c & 1 != 0 {
249 let mut prev = dst;
250 for _ in 0..pixel_size {
251 output[dst] = input[src];
252 dst += 1;
253 src += 1;
254 }
255 let mut count = stride - pixel_size;
256 while count > 0 {
257 count -= 1;
258 output[dst] = output[prev].wrapping_sub(input[src]);
259 dst += 1;
260 prev += 1;
261 src += 1;
262 }
263 } else if c & 2 != 0 {
264 let mut prev = dst - stride;
265 let mut count = stride;
266 while count > 0 {
267 count -= 1;
268 output[dst] = output[prev].wrapping_sub(input[src]);
269 dst += 1;
270 prev += 1;
271 src += 1;
272 }
273 } else {
274 for _ in 0..pixel_size {
275 output[dst] = input[src];
276 dst += 1;
277 src += 1;
278 }
279 let mut prev = dst - stride;
280 let mut count = stride - pixel_size;
281 while count > 0 {
282 count -= 1;
283 output[dst] = (((output[prev] as u16)
284 .wrapping_add(output[dst - pixel_size] as u16)
285 / 2) as u8)
286 .wrapping_sub(input[src]);
287 dst += 1;
288 prev += 1;
289 src += 1;
290 }
291 }
292 }
293 Ok(output)
294 }
295}
296
297pub struct PgdWriter {
298 data: ImageData,
299 method: u32,
300 fake_compress: bool,
301}
302
303impl PgdWriter {
304 pub fn new(data: ImageData, fake_compress: bool) -> Self {
305 Self {
306 data,
307 method: 3,
308 fake_compress,
309 }
310 }
311
312 pub fn with_method(mut self, method: u32) -> Self {
313 self.method = method;
314 self
315 }
316
317 pub fn pack_ge<W: Write>(mut self, mut writer: W) -> Result<()> {
318 let data = match self.method {
319 3 => self.process3()?,
320 _ => panic!("Unsupported GE mode: {}", self.method),
321 };
322 let unpacked_len = data.len() as u32;
323 let compressed = if self.fake_compress {
324 ge_fake_compress(&data)?
325 } else {
326 ge_compress(&data)?
327 };
328 let packed_len = compressed.len() as u32;
329 writer.write_u32(unpacked_len)?;
330 writer.write_u32(packed_len)?;
331 writer.write_all(&compressed)?;
332 Ok(())
333 }
334
335 fn process3(&mut self) -> Result<Vec<u8>> {
336 let bpp = self.data.color_type.bpp(8) as usize;
337 let width = self.data.width as u16;
338 let height = self.data.height as u16;
339 let mut data = MemWriter::new();
340 data.write_u16(0)?; data.write_u16(bpp as u16)?;
342 data.write_u16(width)?;
343 data.write_u16(height)?;
344 data.write_all(&self.process_pal()?)?;
345 Ok(data.into_inner())
346 }
347
348 fn process_pal(&mut self) -> Result<Vec<u8>> {
349 let bpp = match self.data.color_type {
350 ImageColorType::Bgr => 3,
351 ImageColorType::Bgra => 4,
352 ImageColorType::Rgb => {
353 convert_rgb_to_bgr(&mut self.data)?;
354 3
355 }
356 ImageColorType::Rgba => {
357 convert_rgba_to_bgra(&mut self.data)?;
358 4
359 }
360 _ => {
361 return Err(anyhow::anyhow!(
362 "Unsupported color type for palettized PGD: {:?}",
363 self.data.color_type
364 ));
365 }
366 };
367 let ctl = vec![1u8; self.data.height as usize];
369 let stride = self.data.width as usize * bpp;
370 let mut output = vec![0u8; stride * self.data.height as usize];
371 let mut dst = 0;
372 for _ in 0..self.data.height as usize {
373 let mut prev = dst;
374 for _ in 0..bpp {
375 output[dst] = self.data.data[dst];
376 dst += 1;
377 }
378 let mut count = stride - bpp;
379 while count > 0 {
380 count -= 1;
381 output[dst] = self.data.data[prev].wrapping_sub(self.data.data[dst]);
382 dst += 1;
383 prev += 1;
384 }
385 }
386 let mut result = Vec::with_capacity(ctl.len() + output.len());
387 result.extend_from_slice(&ctl);
388 result.extend_from_slice(&output);
389 Ok(result)
390 }
391}
392
393fn ge_fake_compress(data: &[u8]) -> Result<Vec<u8>> {
394 let mut output = Vec::new();
395 let mut pos = 0;
396 let data_len = data.len();
397
398 while pos < data_len {
399 output.push(0u8);
402
403 for _ in 0..8 {
405 if pos >= data_len {
406 break;
407 }
408
409 let chunk_size = std::cmp::min(255, data_len - pos);
411
412 output.push(chunk_size as u8);
414
415 output.extend_from_slice(&data[pos..pos + chunk_size]);
417
418 pos += chunk_size;
419 }
420 }
421
422 Ok(output)
423}
424
425fn ge_compress(data: &[u8]) -> Result<Vec<u8>> {
427 const MIN_MATCH: usize = 4;
428 const MAX_LEN: usize = 0x7FF + 4; const MAX_DIST: usize = 0xFFF; const HASH_BITS: usize = 16;
431 const HASH_SIZE: usize = 1 << HASH_BITS;
432
433 #[inline(always)]
434 fn hash3(bytes: &[u8]) -> usize {
435 let v = ((bytes[0] as u32) << 16) ^ ((bytes[1] as u32) << 8) ^ (bytes[2] as u32);
437 (v.wrapping_mul(0x9E3779B1) >> (32 - HASH_BITS)) as usize
438 }
439
440 let n = data.len();
441 if n == 0 {
442 return Ok(Vec::new());
443 }
444
445 let mut out = Vec::with_capacity(n / 2 + 16);
446
447 let mut ctrl_pos = out.len();
449 out.push(0u8); let mut ctrl: u8 = 0;
451 let mut ctrl_cnt: u8 = 0;
452
453 let mut head = vec![usize::MAX; HASH_SIZE];
455
456 let mut lit_start = 0usize;
458 let mut lit_len = 0usize;
459
460 #[inline(always)]
462 fn start_block(buf: &mut Vec<u8>, ctrl_pos: &mut usize, ctrl: &mut u8, ctrl_cnt: &mut u8) {
463 *ctrl = 0;
464 *ctrl_cnt = 0;
465 *ctrl_pos = buf.len();
466 buf.push(0u8);
467 }
468
469 #[inline(always)]
471 fn flush_ctrl(buf: &mut Vec<u8>, ctrl_pos: usize, ctrl: u8) {
472 if let Some(slot) = buf.get_mut(ctrl_pos) {
473 *slot = ctrl;
474 }
475 }
476
477 let flush_literals = |out: &mut Vec<u8>,
479 ctrl: &mut u8,
480 ctrl_cnt: &mut u8,
481 ctrl_pos: &mut usize,
482 lit_start: &mut usize,
483 lit_len: &mut usize| {
484 while *lit_len > 0 {
485 if *ctrl_cnt == 8 {
486 flush_ctrl(out, *ctrl_pos, *ctrl);
487 start_block(out, ctrl_pos, ctrl, ctrl_cnt);
488 }
489 let chunk = std::cmp::min(255, *lit_len);
490 out.push(chunk as u8);
491 out.extend_from_slice(&data[*lit_start..*lit_start + chunk]);
492 *lit_start += chunk;
493 *lit_len -= chunk;
494 *ctrl_cnt += 1; }
496 };
497
498 let mut pos = 0usize;
499
500 while pos < n {
501 let mut best_len = 0usize;
503 let mut best_dist = 0usize;
504
505 if pos + MIN_MATCH <= n {
506 let h = hash3(&data[pos..pos + 3]);
507 let cand = head[h];
508 if cand != usize::MAX && cand < pos {
509 let dist = pos - cand;
510 if dist > 0 && dist <= MAX_DIST {
511 let max_len = std::cmp::min(MAX_LEN, n - pos);
513 let mut l = 0usize;
515 while l < max_len && data[cand + l] == data[pos + l] {
516 l += 1;
517 }
518 if l >= MIN_MATCH {
519 best_len = l;
520 best_dist = dist;
521 }
522 }
523 }
524 }
525
526 if best_len >= MIN_MATCH {
527 flush_literals(
529 &mut out,
530 &mut ctrl,
531 &mut ctrl_cnt,
532 &mut ctrl_pos,
533 &mut lit_start,
534 &mut lit_len,
535 );
536
537 if ctrl_cnt == 8 {
539 flush_ctrl(&mut out, ctrl_pos, ctrl);
540 start_block(&mut out, &mut ctrl_pos, &mut ctrl, &mut ctrl_cnt);
541 }
542
543 let l = best_len.min(MAX_LEN);
544 let dist = best_dist;
545
546 let len_minus4 = l - 4;
548 let mut word: u16 = ((dist as u16) << 4) as u16;
549 if len_minus4 <= 7 {
550 word |= (len_minus4 as u16) | 0x8;
552 out.push((word & 0xFF) as u8);
553 out.push((word >> 8) as u8);
554 } else {
555 word |= ((len_minus4 >> 8) as u16) & 0x7;
557 out.push((word & 0xFF) as u8);
558 out.push((word >> 8) as u8);
559 out.push((len_minus4 & 0xFF) as u8);
560 }
561
562 let bit = 1u8.wrapping_shl(ctrl_cnt as u32);
564 ctrl |= bit;
565 ctrl_cnt += 1;
566
567 let end = pos + l;
569 let upd_end = end
570 .saturating_sub(MIN_MATCH - 1)
571 .min(n.saturating_sub(MIN_MATCH));
572 let mut p = pos;
573 while p <= upd_end {
574 let h = hash3(&data[p..p + 3]);
575 head[h] = p;
576 p += 1;
577 }
578
579 pos += l;
580 lit_start = pos;
581 lit_len = 0;
582 } else {
583 if pos + 2 < n {
585 let h = hash3(&data[pos..pos + 3]);
586 head[h] = pos;
587 }
588 pos += 1;
589 lit_len += 1;
590
591 if lit_len == 255 {
593 flush_literals(
594 &mut out,
595 &mut ctrl,
596 &mut ctrl_cnt,
597 &mut ctrl_pos,
598 &mut lit_start,
599 &mut lit_len,
600 );
601 }
602 }
603 }
604
605 if lit_len > 0 {
607 flush_literals(
608 &mut out,
609 &mut ctrl,
610 &mut ctrl_cnt,
611 &mut ctrl_pos,
612 &mut lit_start,
613 &mut lit_len,
614 );
615 }
616 flush_ctrl(&mut out, ctrl_pos, ctrl);
617
618 Ok(out)
619}